﻿/*
 * @author: wizardc
 */

using System;
using System.Collections.Generic;
using Dou.Extend;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;

namespace Dou.UI
{
    /// <summary>
    /// 循环列表
    /// 1. 列表项不使用 UGUI 内置的布局组件，采用的是虚拟布局；
    /// 2. 列表项的每一项尺寸都必须一致，不支持不一致的情况；
    /// 3. 只支持 2 向滚动，不支持 4 向滚动；
    /// 4. 不支持直接设置数据源，只能通过设置数据源数量的方式来使用；
    /// </summary>
    [DisallowMultipleComponent]
    public class LoopList : UIBehaviour, ILoopList
    {
        /// <summary>
        /// Item 创建时回调
        /// </summary>
        public event Action<GameObject, int> onItemInit;

        /// <summary>
        /// Item 生效时回调
        /// </summary>
        public event Action<GameObject, int> onItemEnable;
        
        /// <summary>
        /// Item 更新时回调
        /// </summary>
        public event Action<GameObject, int> onItemUpdate;
        
        /// <summary>
        /// Item 失效时回调
        /// </summary>
        public event Action<GameObject, int> onItemDisable;
        
        /// <summary>
        /// Item 模板
        /// </summary>
        public GameObject template;

        /// <summary>
        /// 滚动组件
        /// </summary>
        public ScrollRect scrollRect;
        
        /// <summary>
        /// 布局组件
        /// </summary>
        public VirtualLayoutBase layout;

        private bool _inited = false;
        private Vector2 _templateSize;
        private bool _forceRefresh = false;

        private Dictionary<int, GameObject> _goMap = new Dictionary<int, GameObject>();
        private Queue<GameObject> _goPool = new Queue<GameObject>();
        private HashSet<int> _validIndex = new HashSet<int>();
        private HashSet<int> _invalidIndex = new HashSet<int>();
        
        public ScrollRect GetScrollRect()
        {
            return scrollRect;
        }
        
        protected override void Awake()
        {
            if (template != null)
            {
                template.SetActive(false);
            }
            Init();
        }
        
        private void Init()
        {
            if (_inited)
            {
                return;
            }
            _inited = true;
            
            if (template != null)
            {
                _templateSize = template.GetComponent<RectTransform>().sizeDelta;
            }
            scrollRect.onValueChanged.AddListener(OnScroll);
            layout.Init(this, _templateSize);
        }

        private void OnScroll(Vector2 pos)
        {
            Init();
            RefreshContent();
        }

        protected override void OnRectTransformDimensionsChange()
        {
            OnViewportSizeChanged();
        }

        private void OnViewportSizeChanged()
        {
            Init();
            RefreshContent(true);
        }

        /// <summary>
        /// 设置列表项数据
        /// </summary>
        public void SetListData(int dataCount)
        {
            Init();
            layout.SetItemCount(dataCount);
            RefreshContent(false, true);
        }

        public void SetListData(int dataCount, int jumpIndex)
        {
            Init();
            layout.SetItemCount(dataCount);
            JumpToIndex(jumpIndex);
            RefreshContent(false, true);
        }

        public void SetListData(int dataCount, Vector2 jumpPos)
        {
            Init();
            layout.SetItemCount(dataCount);
            JumpToPos(jumpPos);
            RefreshContent(false, true);
        }

        private void RefreshContent(bool sizeChanged = false, bool forceRefresh = false)
        {
            _validIndex.Clear();
            layout.OnScroll(sizeChanged);
            _forceRefresh = forceRefresh;
            layout.BuildLayout(LayoutGetGameObject);
            _forceRefresh = false;
            RecycleGameObject();
        }

        private GameObject LayoutGetGameObject(int index)
        {
            _validIndex.Add(index);

            if (_goMap.ContainsKey(index))
            {
                var go = _goMap[index];
                if (_forceRefresh)
                {
                    onItemUpdate?.Invoke(go, index);
                }
                return go;
            }
            
            if (_goPool.Count > 0)
            {
                var go = _goPool.Dequeue();
                go.name = index.ToString();
                _goMap.Add(index, go);
                go.SetActive(true);
                onItemEnable?.Invoke(go, index);
                onItemUpdate?.Invoke(go, index);
                return go;
            }

            var newGO = GameObject.Instantiate(template, scrollRect.content);
            newGO.name = index.ToString();
            layout.InitItem(newGO.GetComponent<RectTransform>());
            _goMap.Add(index, newGO);
            newGO.SetActive(true);
            onItemInit?.Invoke(newGO, index);
            onItemUpdate?.Invoke(newGO, index);
            return newGO;
        }

        private void RecycleGameObject()
        {
            _invalidIndex.Clear();
            foreach (var kv in _goMap)
            {
                var index = kv.Key;
                var go = kv.Value;
                if (!_validIndex.Contains(index))
                {
                    _invalidIndex.Add(index);
                    onItemDisable?.Invoke(go, index);
                    go.SetActive(false);
                    _goPool.Enqueue(go);
                }
            }
            foreach (var index in _invalidIndex)
            {
                _goMap.Remove(index);
            }
        }

        /// <summary>
        /// 跳转到指定位置
        /// </summary>
        public void JumpToPos(Vector2 pos)
        {
            layout.JumpToPos(pos);
            RefreshContent();
        }
        
        /// <summary>
        /// 跳转到指定 Item 位置
        /// </summary>
        public void JumpToIndex(int index)
        {
            var pos = GetPosByIndex(index);
            JumpToPos(pos);
        }

        /// <summary>
        /// 获取所有内容的总尺寸
        /// </summary>
        public Vector2 GetContentSize()
        {
            return scrollRect.content.rect.size;
        }
        
        /// <summary>
        /// 获取所有内容的总尺寸
        /// </summary>
        public Vector2 GetContentSize(int dataCount)
        {
            layout.SetItemCount(dataCount);
            return GetContentSize();
        }
        
        /// <summary>
        /// 获取指定 Item 的位置
        /// </summary>
        public Vector2 GetPosByIndex(int index)
        {
            return layout.GetPosByIndex(index);
        }

        /// <summary>
        /// 获取指定索引处的 Item, 可能为空
        /// </summary>
        public GameObject GetItemByIndex(int index)
        {
            if (_goMap.ContainsKey(index))
            {
                return _goMap[index];
            }
            return null;
        }
        
        protected override void OnDestroy()
        {
            foreach (var gameObject in _goPool)
            {
                gameObject.DestroySelf();
            }
        }
        
        /// <summary>
        /// 清除所有数据
        /// </summary>
        public void Clear()
        {
            foreach (var kv in _goMap)
            {
                kv.Value.DestroySelf();
            }
            _goMap.Clear();
            foreach (var gameObject in _goPool)
            {
                gameObject.DestroySelf();
            }
            _goPool.Clear();
            _validIndex.Clear();
            _invalidIndex.Clear();
        }
    }
}
